#ifndef __VANILA_OBJECT_HH__
#define __VANILA_OBJECT_HH__

#include "vanila/chunk.h"
#include "vanila/value.h"
#include <cstdint>
#include <string>
#include <iostream>
#include <map>

namespace vanila
{
//! \brief object type enum
enum class ObjectType
{
    BOUND_METHOD,
    CLASS,
    CLOSURE,
    DICTIONARY,
    FUNCTION,
    INSTANCE,
    LIST,
    UPVALUE,
    NATIVE,
    SET,
    STRING,
};

//! \brief Object class
class Object
{
    static const char* labels[];
public:
    static const char* label(ObjectType type) noexcept;

public:
    Object(ObjectType type);

    virtual ~Object() noexcept = default;

#define OBJECT_IS_AND_AS_SPECIFY_OBJECT(ObjectName, ObjectType)\
    bool is##ObjectName() const noexcept\
    { return this->_type == ObjectType; }\
\
    Object##ObjectName* as##ObjectName() noexcept\
    { return reinterpret_cast<Object##ObjectName*>(this); }\
\
    const Object##ObjectName* as##ObjectName() const noexcept\
    { return reinterpret_cast<const Object##ObjectName*>(this); }

public:
    OBJECT_IS_AND_AS_SPECIFY_OBJECT(BoundMethod, ObjectType::BOUND_METHOD)
    OBJECT_IS_AND_AS_SPECIFY_OBJECT(Class, ObjectType::CLASS)
    OBJECT_IS_AND_AS_SPECIFY_OBJECT(Closure, ObjectType::CLOSURE)
    OBJECT_IS_AND_AS_SPECIFY_OBJECT(Dictionary, ObjectType::DICTIONARY)
    OBJECT_IS_AND_AS_SPECIFY_OBJECT(Function, ObjectType::FUNCTION)
    OBJECT_IS_AND_AS_SPECIFY_OBJECT(Instance, ObjectType::INSTANCE)
    OBJECT_IS_AND_AS_SPECIFY_OBJECT(List, ObjectType::LIST)
    OBJECT_IS_AND_AS_SPECIFY_OBJECT(Upvalue, ObjectType::UPVALUE)
    OBJECT_IS_AND_AS_SPECIFY_OBJECT(Native, ObjectType::NATIVE)
    OBJECT_IS_AND_AS_SPECIFY_OBJECT(String, ObjectType::STRING)
    OBJECT_IS_AND_AS_SPECIFY_OBJECT(Set, ObjectType::SET)

public:
    void mark() noexcept
    { this->_isMarked = true; }

    void unmark() noexcept
    { this->_isMarked = false; }

    virtual void print(bool callStr = true) const = 0;

public:
    //! \brief get object's hash value
    virtual int64_t hash() const;

    //! \brief wheather this == that
    virtual bool equal(const Object* that) const;

    //! \brief wheather this < that
    virtual bool less(const Object* that) const;

    //! \brief wheather this > that
    virtual bool greater(const Object* that) const;

public:
    //! \brief add two object
    virtual Value add(const Object* that) const;

    //! \brief sub two object
    virtual Value sub(const Object* that) const;

    //! \brief mul two object
    virtual Value mul(const Object* that) const;

    //! \brief div two object
    virtual Value div(const Object* that) const;

protected:
    ObjectType _type;
    bool _isMarked;

public:
    ObjectType type() const noexcept
    { return this->_type; }

    bool isMarked() const noexcept
    { return this->_isMarked; }
};

}

#endif